| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- 'use client';
- import Link from 'next/link';
- import { useState } from 'react';
- import { Heart, MoreHorizontal, CornerDownRight } from 'lucide-react';
- import { fetchApi } from '@/lib/utils/client';
- import useAuth from '@/hooks/useAuth';
- import { formatDate } from '@/lib/utils/client';
- import { FeedReply } from '@/types/feed/post';
- import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger
- } from '@/components/ui/dropdown-menu';
- type Props = {
- reply: FeedReply;
- onReply: (target: FeedReply) => void;
- onDelete: (id: number) => void;
- };
- export default function FeedReplyItem({ reply, onReply, onDelete }: Props) {
- const { loginCheck } = useAuth();
- const [likes, setLikes] = useState(reply.likes);
- const [liked, setLiked] = useState(reply.hasLike);
- const [busy, setBusy] = useState(false);
- const handleLike = async () => {
- if (!loginCheck() || busy || reply.isDeleted) {
- return;
- }
- setBusy(true);
- try {
- const res = await fetchApi<{ hasLike: boolean; likes: number }>(`/api/feed/comment/${reply.id}/like`, {
- method: 'POST',
- silent: true
- });
- if (res.success && res.data) {
- setLiked(res.data.hasLike);
- setLikes(res.data.likes);
- }
- } catch (err) {
- console.error(err);
- } finally {
- setBusy(false);
- }
- };
- const handleDelete = async () => {
- if (!confirm('이 댓글을 삭제할까요?')) {
- return;
- }
- try {
- const res = await fetchApi(`/api/feed/comment/${reply.id}`, {
- method: 'DELETE',
- silent: true
- });
- if (res.success) {
- onDelete(reply.id);
- }
- } catch (err) {
- console.error(err);
- }
- };
- const authorDisplay = reply.authorName || reply.authorSID || '알 수 없음';
- const avatarInitial = (authorDisplay.charAt(0) || '?').toUpperCase();
- const parentName = reply.parentAuthorName || reply.parentAuthorSID;
- return (
- <article className={`feed__reply${reply.parentID ? ' feed__reply--nested' : ''}`}>
- {reply.authorSID ? (
- <Link href={`/user/${reply.authorSID}`} className="feed__reply-avatar" aria-label={`${authorDisplay} 프로필`}>
- {reply.authorThumb ? (
- <img src={reply.authorThumb} alt={authorDisplay} />
- ) : (
- <span className="feed__reply-avatar-fallback">{avatarInitial}</span>
- )}
- </Link>
- ) : (
- <span className="feed__reply-avatar">
- <span className="feed__reply-avatar-fallback">{avatarInitial}</span>
- </span>
- )}
- <div className="feed__reply-body">
- <header className="feed__reply-header">
- {reply.authorSID ? (
- <Link href={`/user/${reply.authorSID}`} className="feed__reply-author">{authorDisplay}</Link>
- ) : (
- <span className="feed__reply-author">{authorDisplay}</span>
- )}
- <span className="feed__reply-time">· {formatDate(reply.createdAt)}</span>
- {reply.isOwner && !reply.isDeleted && (
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <button type="button" className="feed__reply-more" aria-label="답글 메뉴">
- <MoreHorizontal size={16} />
- </button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- <DropdownMenuItem onClick={handleDelete}>삭제</DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- )}
- </header>
- {parentName && reply.parentID && (
- <div className="feed__reply-parent-ref">
- <CornerDownRight size={12} />
- <span>{parentName}님에게</span>
- </div>
- )}
- {reply.isDeleted ? (
- <p className="feed__reply-content feed__reply-content--deleted">삭제된 답글입니다.</p>
- ) : (
- <p className="feed__reply-content">{reply.content}</p>
- )}
- {!reply.isDeleted && (
- <footer className="feed__reply-actions">
- <button type="button" className={`feed__reply-action${liked ? ' feed__reply-action--active' : ''}`} onClick={handleLike} aria-pressed={liked}>
- <Heart size={14} fill={liked ? 'currentColor' : 'none'} />
- {likes > 0 && <span>{likes}</span>}
- </button>
- <button type="button" className="feed__reply-action" onClick={() => onReply(reply)}>답글</button>
- </footer>
- )}
- </div>
- </article>
- );
- }
|